package de.bht.cge.flightsim.game;


import java.awt.Point;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import de.bht.cge.flightsim.gfx.CockpitOverlay;
import de.bht.cge.flightsim.gfx.TextOverlay;
import de.bht.cge.flightsim.util.Helper;
import de.bht.cge.flightsim.util.Settings;
import de.bht.jvr.core.Transform;
import de.bht.jvr.core.pipeline.Pipeline;
import de.bht.jvr.input.KeyEvent;
import de.bht.jvr.input.KeyListener;
import de.bht.jvr.logger.Log;
import de.bht.jvr.math.Vector3;
import de.bht.jvr.renderer.AwtRenderWindow;
import de.bht.jvr.renderer.RenderWindow;
import de.bht.jvr.renderer.Viewer;


public class Game implements KeyListener
{
	private Viewer viewer;
	private TextOverlay heightOverlay;
	private TextOverlay speedOverlay;
	private CockpitOverlay cockpit;
	private Scene scene;
	
	private boolean end = false;
	
	private double gameSpeed = 0.08;
	private int speed  = 0;
	
	protected Point mousePos = new Point(); 
	protected boolean mouseDragged = false; 
	private float camRotZ = 0.004f;
	private float camRotY = 0.005f;
	private float camRotX = 0.007f;
	


	
	//Set of pressed keys. Codes are used for special keys (esc, space, etc.)
	protected Set<Integer> pressedKeyCodes = Collections.synchronizedSet(new HashSet<Integer>());

	private float rotY = 0.0f;
	private float rotX = 0.0f;
	private float rotZ = 0.0f;
	

	
	private float[] pos;
	
	public void Initialize(int w, int h, int x, int y)
	{
		try 
		{
			this.scene = new Scene();
			this.scene.BuildScene(this.rotX,this.rotY,this.rotZ);
			
			this.CreateWindow(w, h, x, y);
		}
		catch (Exception e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	
	private void ApplyCamera(double delta)
	{		
		this.scene.UpdateSkybox();
		
		
		    if(this.rotZ > 0.0f)
		    {
	            this.rotZ -= 0.002f;
		    }
	        if(this.rotZ < 0.0f)
	        {
	            this.rotZ += 0.002f;
	        }
	        if(this.rotX > 0.0f)
	        {
	            this.rotX -= 0.003f;
	        }
	        if(this.rotX < 0.0f)
	        {
	            this.rotX += 0.003f;
	        }
	        
	        
	         
	        delta *= this.gameSpeed;
	        this.speed = (int)delta; 
	        this.pos = Helper.GetNodeTranslation(this.scene.getCamera());
	        pos[0] -= Math.sin(this.rotY) * Math.cos(-this.rotX) * delta;
	        pos[1] -= Math.sin(-this.rotX) * delta;
	        pos[2] -= Math.cos(this.rotY) * Math.cos(-this.rotX) * delta;
	        this.scene.getCamera().setTransform(Transform.translate(new Vector3(pos)).mul(Transform.rotateZ(this.rotZ)).mul(Transform.rotateY(this.rotY)).mul(Transform.rotateX(this.rotX)));

	}
	
	private void ProcessKeys(double delta)
	{		
		synchronized (pressedKeyCodes)
        {
			delta *= this.gameSpeed;
			delta /= 5.0;
			
			//To avoid some errors
			if(delta < 0.01) { delta = 0.01; }
		
			//Kameraposition
			this.pos = Helper.GetNodeTranslation(this.scene.getCamera());
			
			for(Integer code: pressedKeyCodes)
			{
				switch(code)
				{
					//Escape
					case 27:
					{
						Log.warning(this.getClass(), "ESCAPE");
						this.end = true;
						break;
					}
					//Left
					case 37:
					{		
					    this.rotY += this.camRotY;
					    if(this.rotZ < 0.0f)
                            this.rotZ += this.camRotZ*2;
                        else
                            this.rotZ += this.camRotZ;
                        break;
						
					}
					//Down
					case 40:
					{
					    if(this.rotX < 0.0f)
					        this.rotX += this.camRotX*2;
					    else
					        this.rotX += this.camRotX;
						break;
					}
					//Right
					case 39:
					{
					    this.rotY -= this.camRotY;
					    if(this.rotZ > 0.0f)
					        this.rotZ -= this.camRotZ*2;
					    else
					        this.rotZ -= this.camRotZ;
						break;
					}
					//Up
					case 38:
					{
					    if(this.rotX > 0.0f)
                            this.rotX -= this.camRotX*2;
                        else
                            this.rotX -= this.camRotX;
						break;
					}
					//W
					
					case 87:
					{		
						
						this.gameSpeed += 0.02;
						break;
					}
					//A
					case 65:
					{
						
						break;
					}
					//S
					case 83:
					{
					    this.gameSpeed -= 0.02;
						break;
					}
					//D
					case 68:
					{
						
						break;
					}
					//Space
					case 32:
					{
						System.out.println("##############Test###########");
						//this.scene.getCloud().setAlpha(this.scene.getCloud().getAlpha()+0.1f);
						break;
					}
					//Enter
					case 10:
					{
						
						break;
					}
					//Zoom out (minus)
					case 109:
					case 45:
					{
						
						break;
					}	
				}
			}
        }
	}
	

	
	private void CreateWindow(int w, int h, int x, int y)
	{
		
		//New render window is created (half the screen's size)
        //RenderWindow win = new NewtRenderWindow(this.scene.getPipeline(), w, h);
	    RenderWindow win = new AwtRenderWindow(this.scene.getPipeline(),w,h);
        
        win.setFullscreen(false);
        
        //Window will be centered on screen
        win.setPosition(x, y); 
      
        //We need to catch keyboard and mouse events
        win.addKeyListener(this);
        
        
        //The viewer will render the game
        this.viewer = new Viewer(true, win);
	}
	
	public void Run() throws Exception
	{
		while(this.viewer.isRunning() && !this.end)
		{
			
			long now = System.currentTimeMillis();
			
			//Display scene
        	this.viewer.display();
        	this.displayOverlay();
        	long delta = System.currentTimeMillis() - now;
        	
        	this.scene.UpdateGround();
        	
        	this.scene.UpdateVegetation(); 
        	
        	this.ApplyCamera(delta);
        	this.ProcessKeys(delta);
        	this.scene.UpdateHeight();
		}
	}
	
	private void displayOverlay() throws Exception
	{
		Pipeline p = this.scene.getPipeline();
		
		
		if(this.heightOverlay == null)
            this.heightOverlay = new TextOverlay(0.0625f, Settings.heightOverlayPosition, Settings.heightOverlaySize, Settings.fontOverlay);
    
        if(this.speedOverlay == null)
            this.speedOverlay = new TextOverlay(0.0625f,Settings.speedOverlayPosition,Settings.speedOverlaySize, Settings.fontOverlay);
		
		if(this.cockpit == null)
		    this.cockpit = new CockpitOverlay(Settings.cockpitOverlay,1.0f,p);		
		
		this.heightOverlay.SetScreenText(""+this.scene.getHeight(),p);
		this.speedOverlay.SetScreenText("" + this.speed*10 , p);
	}


    @Override
    public void keyPressed(KeyEvent e)
    {
        pressedKeyCodes.add(e.getKeyCode());
        
    }

    @Override
    public void keyReleased(KeyEvent e)
    {
        pressedKeyCodes.remove(e.getKeyCode());
    }


    @Override
    public void keyTyped(KeyEvent e)
    {
        // TODO Auto-generated method stub
        
    }
    
}
